/**
 * @author Can-Chen
 * @desc 作用域
 * 
 * 对节点的处理，要考虑其作用域的问题。
 * Scope实例会定义该作用域为函数作用域（function）或块级作用域（block）。
 * 每次新建Scope实例，都会为当前节点创建一个全新的“作用域变量空间”（declaration），任何在此作用域内定义的变量都会存放在这个空间当中
 * 此外，新建Scope实例也会保存其父级作用域。
 */

/**
 * @file 全局声明文件
 */
const standardMap = require('./standard');

/**
 * @file 创建一个普通变量
 */
const SimpleValue = require('./value/simpleValue')

function Scope(type, parentScope) {

    /**
     * @desc 作用域类型
     */
    this.type = type;

    /**
     * @desc 父作用域
     */
    this.parentScope = parentScope;

    /**
     * @desc 全局声明函数
     */
    this.globalDeclaration = standardMap;

    /**
     * @desc 作用域声明
     */
    this.declaration = Object.create(null);
}

Scope.prototype = {

    ...Scope.prototype,

    addDeclaration: function (name, value) {

        this.globalDeclaration[name] = new SimpleValue(value);
    },

    /**
     * @description 作用域查找变量定义
     * @param {变量名} name 
     */
    get: function (name) {
        // const getArray = [
        //     [this.declaration[name], this.declaration[name]],
        //     [this.parentScope, this.parentScope?.get()],
        //     [this.globalDeclaration[name], this.globalDeclaration[name]]
        // ];

        // for (const item of getArray) if (item[0]) return item[1];

        // throw new ReferenceError(`${name} is not defined`)

        if (this.declaration[name]) {
            return this.declaration[name]
        } else if (this.parentScope) {
            return this.parentScope.get(name)
        } else if (this.globalDeclaration[name]) {
            return this.globalDeclaration[name]
        }
        throw new ReferenceError(`${name} is not defined`)
    },

    /**
     * @description 变量值
     * @param {变量名} name 
     * @param {变量值} value 
     */
    set: function (name, value) {
        // const setArray = [
        //     [this.declaration[name], this.declaration[name]?.set(value)],
        //     [this.parentScope, this.parentScope?.set(value)],
        //     [this.globalDeclaration[name], this.globalDeclaration[name]?.set(value)]
        // ];

        // for (const item of setArray) if (item[0]) return item[1];

        // throw new ReferenceError(`${name} is not defined`)

        if (this.declaration[name]) {
            this.declaration[name].set(value)
        } else if (this.parentScope) {
            this.parentScope.set(name, value)
        } else if (this.globalDeclaration[name]) {
            return this.globalDeclaration.set(name, value)
        } else {
            throw new ReferenceError(`${name} is not defined`)
        }
    },

    /**
     * 
     * @param {变量名} name 
     * @param {变量内容} value 
     * @param {声明变量的关键字} kind 
     */
    declare: function (name, value, kind = 'var') {
        const decla = ['var', 'let', 'const'];
        const declaMethod = [this.varDeclare, this.letDeclare, this.constDeclare];

        if (decla.findIndex(kind) > 0) return declaMethod[decla.findIndex(kind)](name, value);

        throw new Error(`Invalid Variable Declaration Kind of "${kind}"`)
    },

    varDeclare: function (name, value) {
        let scope = this;

        // 若当前作用域存在非函数类型的父级作用域时，就把变量定义到父级作用域
        while (scope.parentScope && scope.type !== 'function') {
            scope = scope.parentScope
        };

        scope.declaration[name] = new SimpleValue(value, 'var')
        return scope.declaration[name]
    },

    letDeclare: function (name, value) {
        // 不允许重复定义
        if (this.declaration[name]) {
            throw new SyntaxError(`Identifier ${name} has already been declared`)
        }
        this.declaration[name] = new SimpleValue(value, 'let')
        return this.declaration[name]
    },

    constDeclare: function (name, value) {
        // 不允许重复定义
        if (this.declaration[name]) {
            throw new SyntaxError(`Identifier ${name} has already been declared`)
        }
        this.declaration[name] = new SimpleValue(value, 'const')
        return this.declaration[name]
    }
}

module.exports = Scope;
